home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / fileutil.13 / fileutil / fileutils-3.13 / src / mvdir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-24  |  6.3 KB  |  238 lines

  1. /* mvdir -- rename directory on System V r<4
  2.    Copyright (C) 90, 91, 95, 1996 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software Foundation,
  16.    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  17.  
  18. /* Helper program for GNU mv on systems that lack the rename system call.
  19.  
  20.    Usage: mvdir from to
  21.  
  22.    FROM must be an existing directory.
  23.    TO must not exist, but its parent must exist.
  24.    This program performs the necessary sanity checking on its arguments.
  25.  
  26.    Must be installed setuid root.
  27.  
  28.    Ian Dall (ian@sibyl.eleceng.ua.oz.au)
  29.    and David MacKenzie (djm@gnu.ai.mit.edu) */
  30.  
  31. #include <config.h>
  32. #include <stdio.h>
  33. #include <getopt.h>
  34. #include <sys/types.h>
  35. #include <signal.h>
  36.  
  37. #include "system.h"
  38. #include "save-cwd.h"
  39. #include "error.h"
  40.  
  41. #ifndef HIPRI
  42. #define HIPRI -10
  43. #endif
  44.  
  45. #ifdef DEBUG
  46. #define link(FROM, TO) (printf("Linking %s to %s\n", FROM, TO), 0)
  47. #define unlink(FILE) (printf("Unlinking %s\n", FILE), 0)
  48. #endif
  49.  
  50. /* The name this program was run with. */
  51. char *program_name;
  52.  
  53. char *xgetcwd ();
  54.  
  55. char *basename ();
  56. char *dirname ();
  57. char *xmalloc ();
  58. void strip_trailing_slashes ();
  59.  
  60. /* If nonzero, display usage information and exit.  */
  61. static int show_help;
  62.  
  63. /* If nonzero, print the version on standard output and exit.  */
  64. static int show_version;
  65.  
  66. static struct option const long_options[] =
  67. {
  68.   {"help", no_argument, &show_help, 1},
  69.   {"version", no_argument, &show_version, 1},
  70.   {0, 0, 0, 0}
  71. };
  72.  
  73. static void
  74. usage (int status)
  75. {
  76.   if (status != 0)
  77.     fprintf (stderr, _("Try `%s --help' for more information.\n"),
  78.          program_name);
  79.   else
  80.     {
  81.       printf (_("Usage: %s [OPTION]... EXISTING_DIR NEW_DIR\n"), program_name);
  82.       printf (_("\
  83. Rename EXISTING_DIR to NEW_DIR.\n\
  84. \n\
  85.    --help      display this help and exit\n\
  86.    --version   output version information and exit\n"));
  87.     }
  88.   exit (status);
  89. }
  90.  
  91. /* Return the full pathname (from /) of the directory DIR,
  92.    in malloc'd storage. */
  93.  
  94. static char *
  95. fullpath (const char *dir)
  96. {
  97.   struct saved_cwd cwd;
  98.   char *path;
  99.  
  100.   if (save_cwd (&cwd))
  101.     exit (1);
  102.  
  103.   path = xgetcwd () ;
  104.   if (path == NULL)
  105.     error (1, errno, _("cannot get current directory"));
  106.  
  107.   if (restore_cwd (&cwd, _("starting directory"), NULL))
  108.     exit (1);
  109.  
  110.   free_cwd (&cwd);
  111.  
  112.   return path;
  113. }
  114.  
  115. int
  116. main (int argc, char **argv)
  117. {
  118.   char *from, *from_parent, *from_base;
  119.   char *to, *to_parent, *to_parent_path, *to_base, *to_dotdot;
  120.   struct stat from_stats, to_stats;
  121.   char *slash;
  122.   int c, i;
  123.  
  124.   program_name = argv[0];
  125.   setlocale (LC_ALL, "");
  126.   bindtextdomain (PACKAGE, LOCALEDIR);
  127.   textdomain (PACKAGE);
  128.  
  129.   while ((c = getopt_long (argc, argv, "", long_options, (int *) 0)) != EOF)
  130.     {
  131.       switch (c)
  132.     {
  133.     case 0:
  134.       break;
  135.  
  136.     default:
  137.       usage (2);
  138.     }
  139.     }
  140.  
  141.   if (show_version)
  142.     {
  143.       printf ("mvdir - %s\n", PACKAGE_VERSION);
  144.       exit (0);
  145.     }
  146.  
  147.   if (show_help)
  148.     usage (0);
  149.  
  150.   if (argc - optind != 2)
  151.     usage (2);
  152.  
  153.   from = argv[optind];
  154.   to = argv[optind + 1];
  155.   strip_trailing_slashes (from);
  156.   strip_trailing_slashes (to);
  157.   from_parent = dirname (from);
  158.   to_parent = dirname (to);
  159.   if (!from_parent || !to_parent)
  160.     error (2, 0, _("virtual memory exhausted"));
  161.  
  162.   /* Make sure `from' is not "." or "..". */
  163.   from_base = basename (from);
  164.   if (!strcmp (from_base, ".") || !strcmp (from_base, ".."))
  165.     error (1, 0, _("cannot rename `.' or `..'"));
  166.  
  167.   /* Even with an effective uid of root, link fails if the target exists.
  168.      That is what we want, so don't unlink `to' first.
  169.      However, we do need to check that the directories that link and unlink
  170.      will modify exist and are writable by the user. */
  171.  
  172.   if (stat (from, &from_stats))
  173.     error (1, errno, "%s", from);
  174.   if (!S_ISDIR (from_stats.st_mode))
  175.     error (1, 0, _("`%s' is not a directory"), from);
  176.   if (access (from_parent, W_OK))
  177.     error (1, errno, _("cannot write to `%s'"), from_parent);
  178.   if (access (to_parent, W_OK))
  179.     error (1, errno, _("cannot write to `%s'"), to_parent);
  180.  
  181.   /* To prevent disconnecting the tree rooted at `from' from its parent,
  182.      quit if any of the directories in `to' are the same (dev and ino)
  183.      as the directory `from'. */
  184.  
  185.   slash = to_parent_path = fullpath (to_parent);
  186.   while (1)
  187.     {
  188.       while (*slash == '/')
  189.     ++slash;
  190.       slash = strchr (slash, '/');
  191.       if (slash != NULL)
  192.     *slash = '\0';
  193.  
  194.       if (stat (to_parent_path, &to_stats))
  195.     error (1, errno, "%s", to_parent_path);
  196.       if (to_stats.st_dev == from_stats.st_dev
  197.       && to_stats.st_ino == from_stats.st_ino)
  198.     error (1, 0, _("`%s' is an ancestor of `%s'"), from, to);
  199.       if (slash != NULL)
  200.     *slash++ = '/';
  201.       else
  202.     break;
  203.     }
  204.  
  205.   /* We can't make the renaming atomic, but we do our best. */
  206.   for (i = NSIG; i > 0; i--)
  207.     if (i != SIGKILL)
  208.       signal (i, SIG_IGN);
  209.   setuid (0);            /* Make real uid 0 so it is harder to kill. */
  210.   nice (HIPRI - nice (0));    /* Raise priority. */
  211.  
  212.   if (link (from, to))
  213.     error (1, errno, _("cannot link `%s' to `%s'"), from, to);
  214.   if (unlink (from))
  215.     error (1, errno, _("cannot unlink `%s'"), from);
  216.  
  217.   /* Replace the directory's `..' entry.  It used to be a link to
  218.      the parent of `from'; make it a link to the parent of `to' instead.
  219.      To handle the case where `from' is the current directory
  220.      and `to' starts with `../', we go to the full path of `to's parent,
  221.      lest we try to reference the new directory's `..' while creating it.  */
  222.  
  223.   to_base = basename (to);
  224.   i = strlen (to_base);
  225.   to_dotdot = xmalloc (i + 4);
  226.   strcpy (to_dotdot, to_base);
  227.   strcpy (to_dotdot + i, "/..");
  228.  
  229.   if (chdir (to_parent_path))
  230.     error (1, errno, "%s", to_parent_path);
  231.   if (unlink (to_dotdot) && errno != ENOENT)
  232.     error (1, errno, _("cannot unlink `%s'"), to_dotdot);
  233.   if (link (".", to_dotdot))
  234.     error (1, errno, _("cannot link `%s' to `%s'"), ".", to_dotdot);
  235.  
  236.   exit (0);
  237. }
  238.